
#ifndef _SELECTION_H_
#define _SELECTION_H_

#include <GL/glu.h>
#include <list>

const int BUFSIZE=16384;

class Selection {

public:
   Selection() {
      
   }
   
   void begin(int x, int y, int w=8, int h=8) {
      
      (w==0)? (w=2):0;
      (h==0)? (h=2):0;
      
      ///< following code is for object selection
      glGetIntegerv(GL_VIEWPORT, viewport);
      glSelectBuffer(BUFSIZE, selectBuf);
      glRenderMode(GL_SELECT);
      glInitNames();
      
      /// getting the current projection matrix (ortho or perspective)
      glGetFloatv(GL_PROJECTION_MATRIX, projmatrix);
      glGetFloatv(GL_MODELVIEW_MATRIX, mviewmatrix);
      
      /// setting the new projection matrix
      glMatrixMode(GL_PROJECTION);
      glPushMatrix();
      glLoadIdentity();
      gluPickMatrix(x, y, w, h, viewport);
      /// multiplying the original projection matrix (ortho or perspective) with the pick matrix
      glMultMatrixf(projmatrix);
   }
   
   // retorna una lista de ids processada
   bool end(std::list<unsigned int> &idlist) {
      
      glMatrixMode(GL_PROJECTION);
      glPopMatrix();
      glFlush();
      hits = glRenderMode(GL_RENDER);
      /// if not selected 
      if (hits==0) return false;
      
      /// getting the items
      GLuint names, *ptr; int IDitem=-1; int z1[2]={0,0};
      ptr=(GLuint*)selectBuf;
      float ZMIN = 1e10, ZMAX=-1e10;
      for (int i=0; i<hits; i++) {
         names = *ptr++;
         float zmin = ((float)(*ptr)/0x7fffffff); 
         ptr++; 
         //~ float zmax = ((float)(*ptr)/0x7fffffff); 
         ptr++;
         IDitem = *ptr++;
         if (zmin <= ZMIN) {
            ZMIN = zmin; 
            z1[0]=IDitem;
         }
         if (zmin >= ZMAX) {
            ZMAX = zmin; 
            z1[1]=IDitem;
         }
      }
      
      if (z1[0] != z1[1]) 
         idlist.push_back(z1[0]);
      idlist.push_back(z1[1]);
      
      glMatrixMode(GL_MODELVIEW);
      glLoadIdentity();
      glMultMatrixf(mviewmatrix);
      
      return true;
   }
   
   // retorna una lista con todos los ids atingidos
   bool endAll(std::list<unsigned int> &idlist) {
      
      glMatrixMode(GL_PROJECTION);
      glPopMatrix();
      hits = glRenderMode(GL_RENDER);
      //~ std::cout<<"hits hits hits: "<<hits<<std::endl;
      /// if not selected 
      if (hits==0) return false;
      
      /// getting the items
      GLuint names, *ptr; int IDitem=-1; 
      //~ int z1[2]={0,0};
      ptr=(GLuint*)selectBuf;
      //~ float ZMIN = 1e10, ZMAX=-1e10;
      for (int i=0; i<hits; i++) {
         names = *ptr++;
         //~ float zmin = ((float)(*ptr)/0x7fffffff); 
         ptr++; 
         //~ float zmax = ((float)(*ptr)/0x7fffffff); 
         ptr++;
         IDitem = *ptr++;
         //~ if (zmin < ZMIN) {
            //~ ZMIN = zmin; 
            //~ z1[0]=IDitem;
            //~ idlist.push_front(IDitem);
         //~ }
         //~ if (zmin > ZMAX) {
            //~ ZMAX = zmin; 
            //~ z1[1]=IDitem;
            //~ idlist.push_back(IDitem);
         //~ }
         idlist.push_front(IDitem);
      }
      
      //~ if (z1[0] != z1[1]) 
         //~ idlist.push_back(z1[0]);
      //~ idlist.push_back(z1[1]);
      
      glMatrixMode(GL_MODELVIEW);
      glLoadIdentity();
      glMultMatrixf(mviewmatrix);
      
      return true;
   }
   
private:
   
   GLuint      selectBuf[BUFSIZE];   ///
   GLint       viewport[4];          ///
   GLfloat     projmatrix[16];       /// It's used to get and set the projection matrix in selection mode
   GLfloat     mviewmatrix[16];      /// It's used to get and set the projection matrix in selection mode
   GLint       hits;
};

#endif
